mod garden;

use rand::Rng;
use std::{cmp::Ordering, fs, io};
use std::collections::HashMap;
use std::fs::File;
use std::io::ErrorKind;

#[derive(Debug)]
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
    power: u64,
}

impl User {
    fn new(username: String, email: String, sign_in_count: u64, power: u64) -> User {
        User {
            active: true,
            username,
            email,
            sign_in_count,
            power,
        }
    }

    fn power_count(&self) -> u64 {
        self.sign_in_count * self.power
    }

    fn equal(&self, user: &User) -> bool {
        self.power == user.power
    }
}

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

use garden::vegetables::Asparagus;

fn main() {
    let greeting = File::open("hello.txt");
    let g_file = match greeting {
        Ok(file) => {file}
        Err(error) => {match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Tried to create file but there was a problem: {:?}", e),
            },
            other_error => panic!("There was a problem: {:?}", other_error),
        }}
    };

    let x = fs::read_to_string("hello.txt").expect("Unable to read file");
    print!("{:?}",x);
}

fn string_chars_and_hashmap() {
    for c in "a变态hentai".chars() {
        println!("{}", c);
    }

    let field_name = String::from("Favorite color");
    let field_value = String::from("Blue");
    let mut map = HashMap::new();
    map.insert(field_name, field_value);

    for (k, v) in &map {
        println!("{}: {}", k, v);
    }

    let color = map.get(&String::from("Favorite color")).unwrap();
    println!("{:?}", color);
    println!("{:#?}", map);
}

fn enum_match() {
    let home = IpAddr::V4(127, 0, 0, 1);

    let loopback = IpAddr::V6(String::from("::1"));

    let x = value_in_cents(&Coin::Dime);
    println!("{}", x);

    let config_max = Some(3u8);
    println!("Penny: {}", config_max.unwrap());
    if let Some(max) = config_max {
        println!("Max: {}", max);
    }
}

fn value_in_cents(coin: &Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 2,
        Coin::Dime => 3,
        Coin::Quarter => 4,
    }
}

fn struct_func() {
    let user = bind_user(
        String::from("hentai"),
        String::from("hentai-lolicon@bt.com"),
    );
    let mut user2 = bind_user(
        String::from("hentai"),
        String::from("hentai-lolicon@bt.com"),
    );
    user2.sign_in_count = 8;
    println!("user: {:#?}", user);
    let i = user.power_count();
    println!("user power count: {}", i);
    let i1 = User::power_count(&user);
    println!("user power count: {}", i1);
    let i2 = user2.power_count();
    println!("user power count: {}", i2);
    let x = user.equal(&user2);
    println!("user equal: {}", x);
}

fn bind_user(username: String, email: String) -> User {
    User {
        active: true,
        username,
        email,
        sign_in_count: 6,
        power: 25,
    }
}

fn user_fill(user: User) -> User {
    User {
        active: true,
        sign_in_count: 0,
        ..user
    }
}

fn slice_type() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    println!("The first word is: {}", word);
    s.clear();

    let mut list = [1, 2, 3, 4, 5];

    let x = &list[..];
    println!("x: {:?}", x);
}

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }

    &s[..]
}

// 引用，默认不可对原值进行修改，要修改需带 mut 关键字，不可同时多次可变引用, 不能在拥有不可变引用的同时拥有可变引用
fn calculate() {
    let mut s1 = String::from("hello");
    let len = calculate_length(&mut s1);
    println!("The length of '{s1}' is {len}.");
    println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &mut String) -> usize {
    s.push_str(" hentai");
    s.len()
}

// 基本数据类型直接赋值是 copy，非基本数据类型直接赋值是指针移动，需要调用clone()方法，或者引用
fn copy_clone() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("{}, world!", s1);
    println!("{}, world!", s2);
}

fn control_loop() {
    let mut cnt = 0;
    loop {
        cnt += 1;
        println!("loop cnt: {}", cnt);
        if cnt >= 10 {
            break;
        }
    }

    cnt = 0;
    while cnt < 10 {
        println!("while cnt: {}", cnt);
        cnt += 1;
    }

    cnt = 0;
    for cnt in 0..10 {
        println!("for cnt: {}", cnt);
    }
}

fn control_if() {
    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read line");

    let input: i32 = match input.trim().parse() {
        Ok(num) => num,
        Err(_) => {
            println!("Not a number");
            return;
        }
    };

    if input > 0 {
        println!("{} > 0", input);
    } else if input < 0 {
        println!("{} < 0", input);
    } else if input == 0 {
        println!("{} is 0", input);
    }
}

fn function() {
    let res = int_add(1, 2);
    println!("res is {}", res);
}

fn int_add(a: i32, b: i32) -> i32 {
    a + b
}

fn param_unit_type() {
    let x = 5;
    {
        let x = 6;
        println!("The value of x is: {}", x);
    }
    println!("The value of x is: {}", x);

    // unit 元组
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (_x, y, _z) = tup;
    println!("The value of tup y is: {}", y);

    // array list 数组 列表
    let list: [i32; 5] = [1, 2, 3, 4, 5];
    let months = [
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    ];
    for x in list {
        println!("The value of months x is: {}", x);
    }

    for i in 0..months.len() {
        println!("{}", months[i]);
    }
}

fn guess_the_number() {
    println!("Guess the number!");
    let secret_number = rand::thread_rng().gen_range(1..=100);

    loop {
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        println!("You guessed: {}", guess);

        // let guess: u32 = guess.trim().parse().expect("Please type a number!");
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("Please type a number!");
                continue;
            }
        };

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}
